package utils;

import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;


public class ConcurrentTestUtil {

    /**
     * 多线程并发执行某项任务
     *
     * @param concurrentThreads    并发线程数，可以用来模拟并发访问用户数
     * @param times                总共执行多少次
     * @param task                 任务
     * @param resultHandler        结果处理器
     * @param executeTimeoutMillis 执行任务总超时
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public static <T> void concurrentTest(long concurrentThreads, int times, final Callable<T> task,
                                          ResultHandler<T> resultHandler, long executeTimeoutMillis)
            throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool((int) concurrentThreads);
        List<Future<T>> results = new ArrayList<Future<T>>(times);
        long startTimeMillis = System.currentTimeMillis();

        for (int i = 0; i < times; i++) {
            results.add(executor.submit(task));
        }

        executor.shutdown();
//      while(!executor.awaitTermination(1, TimeUnit.SECONDS));   // 每隔1s钟检查线程池是否已关闭
        boolean executeCompleteWithinTimeout = executor.awaitTermination(executeTimeoutMillis,
                TimeUnit.MILLISECONDS);
        if (!executeCompleteWithinTimeout) {
            System.out.println("Execute tasks out of timeout [" + executeTimeoutMillis + "ms]");

            /*
             * 取消所有任务
             */
            for (Future<T> r : results) {
                r.cancel(true);
            }
        } else {
            long totalCostTimeMillis = System.currentTimeMillis() - startTimeMillis;
            // 线程池此时肯定已关闭，处理任务结果
            for (Future<T> r : results) {
                /*
                 * r.get()本义是等待任务执行结果，但这里不需要等待，因为上面已经把线程池关闭了
                 */
                if (resultHandler != null) {
                    resultHandler.handle(r.get());
                }
            }

            System.out.println("concurrent threads: " + concurrentThreads + ", times: "
                    + times);
            System.out.println("total cost time(ms): " + totalCostTimeMillis
                    + "ms, avg time(ms): " + ((double) totalCostTimeMillis / times));
            System.out.println("tps: " + (double) (times * 1000) / totalCostTimeMillis);
        }
    }


    public static String getRandomRequestBrandName() {
        int randomInt = (int) (Math.random() * 5);
        switch (randomInt) {
            case 1:
                return "세컨핑크";
            case 2:
                return "Mamor";
            case 3:
                return "feila";
            default:
                return "brand_abc_00";
        }

    }

    /**
     * 多线程并发执行
     */
    public static <T> void invokeAPIByFixedRate(int invokeRate, int concurrentThreadNum, long totalExecuteMillis) {
        ExecutorService executor = Executors.newFixedThreadPool(concurrentThreadNum);
        int invokeRatePerThread = invokeRate / concurrentThreadNum + 1;
        for (int i = 0; i < concurrentThreadNum; i++) {
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    long startTime = System.currentTimeMillis(), lastTriggerModTime = 0;
                    while (System.currentTimeMillis() - startTime <= totalExecuteMillis) {
                        int invokeCounter = 0;
                        if (invokeCounter >= invokeRatePerThread) {
                            if ((System.currentTimeMillis() - startTime) % 1000 >= lastTriggerModTime) {
                                lastTriggerModTime = (System.currentTimeMillis() - startTime) % 1000;
                                continue;// 时间不够，跳过调用
                            }

                            System.out.println("current Thread: " + Thread.currentThread().getName() + " has success reach current invokeRatePerThread: " + invokeRatePerThread + " and current time: " + (System.currentTimeMillis() - startTime) + " .");
                            invokeCounter = 0;
                        } else {
                            // 一直尚未达到制定调用秒调用次数
                            if ((System.currentTimeMillis() - startTime) % 1000 < lastTriggerModTime) {
                                System.out.println("current Thread: " + Thread.currentThread().getName() + " fail to reach current invokeRatePerThread: " + invokeRatePerThread + " .");
                            }
                        }

                        try {
                            Map<String, String> requestBody = new HashMap<>();
                            requestBody.put("brandName", getRandomRequestBrandName());
                            HttpUtil.sendPost("local.coupangdev.com:8080", "/api/v1/getSellerInfoByBrandName", requestBody);
                            ++invokeCounter;
                        } catch (Exception e) {
                            System.out.println("current post call fail: " + e.getMessage());
                        }

                        lastTriggerModTime = (System.currentTimeMillis() - startTime) % 1000;
                    }
                }
            });
        }

        executor.shutdown();
    }


    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ConcurrentTestUtil.concurrentTest(500, 1500000,
                new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        String result = null;
//                        long startTime = System.currentTimeMillis();
                        try {
                            Map<String, String> requestBody = new HashMap<>();
                            requestBody.put("brandName", getRandomRequestBrandName());
                            result = HttpUtil.sendPost("10.228.37.246:80", "/api/v1/getSellerInfoByBrandName", requestBody);
                        } catch (Exception e) {
                            System.out.println("current post call fail: " + e.getMessage());
                        }

                        return result;
                    }
                },
                new ResultHandler<String>() {
                    @Override
                    public void handle(String result) {
                        System.out.println("invoke result: " + result);
                    }
                }, 6000 * 1000);


//        for (int i = 0; i < 1000; i++) {
//            for (int j = 0; j < 200; j++) {
//                try {
//                    Map<String, String> requestBody = new HashMap<>();
//                    requestBody.put("brandName", getRandomRequestBrandName());
//                    final String result = HttpUtil.sendPost("10.228.32.42:80", "/api/v1/getSellerInfoByBrandName", requestBody);
//                    System.out.println("current resp: " + result);
//                } catch (Exception e) {
//                    System.out.println("current post call fail: " + e.getMessage());
//                }
//            }
//        }


        //        invokeAPIByFixedRate(3000, 10, 10000);


//        ConcurrentTestUtil.concurrentTest(10, 10,
//                new Callable<String>() {
//                    @Override
//                    public String call() throws Exception {
//                        String result = null;
//                        long startTime = System.currentTimeMillis();
//                        int counter = 0;
//                        while (counter < 300) {
//                            try {
//                                Map<String, String> requestBody = new HashMap<>();
//                                requestBody.put("brandName", getRandomRequestBrandName());
//                                result = HttpUtil.sendPost("local.coupangdev.com:8080", "/api/v1/getSellerInfoByBrandName", requestBody);
//                                ++counter;
//                            } catch (Exception e) {
//                                System.out.println("current post call fail: " + e.getMessage());
//                            }
//                        }
//
//                        System.out.println("current Thread: " + Thread.currentThread().getName() + " total invoke count: " + counter + " times.");
//                        return result;
//                    }
//                },
//                new ResultHandler<String>() {
//                    @Override
//                    public void handle(String result) {
////                        System.out.println("result: " + result);
//                    }
//                }, 6000 * 1000);
    }

}
